
package com.studiofortress.sf.structure;

import com.studiofortress.sf.graphics.display.ControlClickEvent;
import com.studiofortress.sf.graphics.display.ControlKeyEvent;
import com.studiofortress.sf.graphics.display.ControlMotionEvent;
import com.studiofortress.sf.structure.collisions.CollisionHandler;
import com.studiofortress.sf.util.collections.BooleanStack;
import com.studiofortress.sf.graphics.GraphicsGL;
import com.studiofortress.sf.graphics.display.ControlKeyCharEvent;
import com.studiofortress.sf.structure.collisions.GridCollisions;
import com.studiofortress.sf.util.collections.CachingHashMap;
import com.studiofortress.sf.util.collections.CallbackIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * The width and height of the World corresponds to the drawing area. It will
 * only draw within the set width and height area and everything outside will
 * not be drawn.
 * 
 * The position of 0, 0 for Actors inside the World is the top-left corner of
 * this World. The World can also scroll to offset this. Scrolling only affects
 * how the World is drawn.
 *
 * The default control handling for the World works in reverse painting order.
 * So the highest Actor will receive the control event before any other.
 *
 * @author Joseph Lenton
 * @param <G> The type of graphics object this world draws too.
 * @param <A> The type of Actor stored inside of this world.
 */
public class World<G extends GraphicsGL, A extends Actor<? super G>> extends Actor<G>
{
    /* This is used in getActors to avoid making an empty array everytime it is
     * called. */
    private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];

    // the collision detection for this World
    private CollisionHandler<A> collisions;
    
    // it should be presumed that these two both contain the same Actors
    // but in different orders
    private SortedActors<A> actActors;
    private SortedActors<A> paintActors;

    // The 'temp' versions of the stack/lists below are for holding an alternate list/stack
    // whilst we are iterating over the first one.
    // Corresponds 1 to 1 against the addRemoveActors to state if the associated actor is being added or removed.
    private BooleanStack addRemoveActorsIsAdd;
    private BooleanStack tempAddRemoveActorsIsAdd;
    // Lists all the actors being added or removed in the order that they are being added/removed.
    private List<A> addRemoveActors;
    private List<A> tempAddRemoveActors;
    
    /* these are caches storing the act and paint for Actors that have been added,
     * but are cached in the 'addingActors' list.
     * One for setting act, one for paint. */
    private Map<A, Integer> addingActorPaint;
    private Map<A, Integer> addingActorAct;

    // scrolling the viewpoint
    private float scrollX;
    private float scrollY;

    private WorldControls<A> controls;

    private MainLoop mainLoop;

    // these are teh callbacks to use instead of iterators
    private final CallbackIterator<A> removeCallback = new CallbackIterator<A>() {
        public void iterate(A actor)
        {
            removeActor( actor );
        }
    };

    private final CallbackIterator<A> actActorsCallback = new CallbackIterator<A>() {
        public void iterate(final A actor)
        {
            if (actor.isAct() && !actor.isAwaitingRemoval()) {
                actor.act();
            }
        }
    };

    private final PaintCallback paintActorsCallback = new PaintCallback();
    
    /* Control Callback Iterators */
    private final ControlEventCallbackPartialIterator<A, ControlClickEvent> handleClickEventIterator =
              new ControlEventCallbackPartialIterator<A, ControlClickEvent>()
    {
        public void iterate(A value)
        {
            value.handleClickEvent( event );
        }
    };

    private final ControlEventCallbackPartialIterator<A, ControlKeyEvent> handleKeyEventIterator =
              new ControlEventCallbackPartialIterator<A, ControlKeyEvent>()
    {
        public void iterate(A value)
        {
            value.handleKeyEvent( event );
        }
    };

    private final ControlEventCallbackPartialIterator<A, ControlKeyCharEvent> handleKeyCharEventIterator =
              new ControlEventCallbackPartialIterator<A, ControlKeyCharEvent>()
    {
        public void iterate(A value)
        {
            value.handleKeyCharEvent( event );
        }
    };

    private final ControlEventCallbackPartialIterator<A, ControlMotionEvent> handleMotionEventIterator =
              new ControlEventCallbackPartialIterator<A, ControlMotionEvent>()
    {
        public void iterate(A value)
        {
            value.handleMotionEvent( event );
        }
    };
    
    /**
     * Creates a new world which has the given width and height.
     * By default the World is placed at width/2, height/2 to ensure it is
     * midhandled within whatever world it will be placed within.
     * @param width The width in pixels of the new world.
     * @param height The height in pixels of the new world.
     */
    public World(int width, int height)
    {
        this(
                width, height, // size
                width/2, height/2); // location
    }
    
    /**
     * Creates this world with the given width and height.
     * @param width The width in pixels of the view of the new world.
     * @param height The height in pixels of the view of the the new world.
     * @param x The location along the x axis of this world within it's World.
     * @param y The location along the y axis of this world within it's World.
     */
    public World(final int width, final int height, final int x, final int y)
    {
        super(x, y, width, height);
        
        collisions = new GridCollisions<A>();
        
        actActors   = new SortedActors<A>();
        paintActors = new SortedActors<A>();
        
        addRemoveActors = new ArrayList<A>();
        tempAddRemoveActors  = new ArrayList<A>();
        addRemoveActorsIsAdd = new BooleanStack();
        tempAddRemoveActorsIsAdd = new BooleanStack();

        addingActorPaint = new CachingHashMap<A, Integer>();
        addingActorAct   = new CachingHashMap<A, Integer>();

        scrollX = 0;
        scrollY = 0;
        
        controls = null;

        mainLoop = null;
    }

    /**
     * For getting the World specific controls object.
     * @return A WorldControls associated with this World.
     */
    public WorldControls<A> getControls()
    {
        if (controls == null) {
            controls = new WorldControlsInner<A>(this);
        }
        return controls;
    }

    /**
     * The MainLoop returned is either the mainloop for this World instance or
     * the MainLoop from one of it's ancestor Worlds. The first MainLoop found
     * is the one returned.
     * @return The MainLoop this world is running within, otherwise null if there is no MainLoop.
     */
    public MainLoop getMainLoop()
    {
        if (mainLoop == null) {
            final World superWorld = getWorld();
            
            if (superWorld != null) {
                return superWorld.getMainLoop();
            } else {
                return null;
            }
        } else {
            return mainLoop;
        }
    }
    
    /**
     * Directly sets the MainLoop for this world. If the MainLoop is
     * different to the current MainLoop then the controls are invalidated
     * and will need to be recreated.
     * As this is an internal call it makes no checks about the usage being
     * correct. This is allow a MainLoop to have complete control when
     * moving a World between MainLoops.
     * @param container The new MainLoop that this World is within.
     */
    void setMainLoop(final MainLoop container)
    {
        if (this.mainLoop != container) {
            this.controls = null;
        }
        this.mainLoop = container;
    }

    /**
     * This does not take into account if a parent of this World is within a
     * MainLoop. Only if this World is directly the World of a MainLoop object,
     * regardless of if that MainLoop is running or not.
     * @return True if this is directly within a MainLoop. False if not.
     */
    boolean isInMainLoop()
    {
        return mainLoop != null;
    }

    /**
     * When a CollisionHandler is set the actors within the old CollisionHandler
     * are all removed from that handler and added to the new one.
     * @param collisions A new collision handler for this World, cannot be null.
     */
    public void setCollisions(final CollisionHandler<A> collisions)
    {
        if (collisions == null) {
            throw new IllegalArgumentException("Collisions cannot be null.");
        }
        
        actActors.iterate(
                new CallbackIterator<A>() {
                    public void iterate(final A actor)
                    {
                        World.this.collisions.remove( actor );
                        collisions.add( actor );
                    }
                }
        );
        
        this.collisions = collisions;
    }
    
    /**
     * Gets all Actors in this world.
     * @return A list of all the contained Actors in this World.
     */
    public List<A> getAllActors()
    {
        final List<A> returnActors = new ArrayList<A>( getActorCount() );
        for (Collection<A> actors : actActors.getActorLayers()) {
            returnActors.addAll( actors );
        }
        return returnActors;
    }
    
    /**
     * The number of Actors in this world.
     * @return The number of actors currently in the world itself.
     */
    public int getActorCount()
    {
        int count = 0;
        for (Collection<A> actors : actActors.getActorLayers()) {
            count += actors.size();
        }
        return count;
    }

    /**
     * This searches for the first Actor that matches the class given.
     * 
     * If multiple there are actors of the given class in this worls then the
     * first one found is returned. There is no guarantee that subsequent calls
     * to this method will always return the same Actor.
     * @param <B> The type of the Actor to return.
     * @param klass The class of the Actor to return.
     * @return The first Actor found that is an instance of the given class, null if no Actor is found.
     */
    public <B> B getActor( Class<B> klass )
    {
        for (Collection<A> actors : actActors.getActorLayers()) {
            for (A actor : actors) {
                if ( klass.isInstance(actor) ) {
                    return (B) actor;
                }
            }
        }
        
        return null;
    }

    /**
     * Given some filters, this will return all of the Actors that match the
     * conditions in all of those filters. If this is given null or an empty
     * array of filters then all actors are returned.
     *
     * If one of the filters given is null then it is ignored.
     * 
     * @param filters The filters that the Actors must conform too, or null to return all actors.
     * @return A list of all the Actors that match the given filter.
     */
    public List<A> getActors(final Filter<A> ... filters)
    {
        if (filters == null || filters.length == 0) {
            return getAllActors();
        } else {
            final List<A> returnActors = new LinkedList<A>();
            final int filtersSize = filters.length;

            actActors.iterate(
                    new CallbackIterator<A>() {
                        public void iterate(final A actor)
                        {
                            boolean match = true;
                            
                            for (int i = 0; i < filtersSize; ++i) {
                                final Filter filter = filters[i];
                                if (filter == null || !filter.accept(actor)) {
                                    match = false;
                                    break;
                                }
                            }

                            if (match) {
                                returnActors.add(actor);
                            }
                        }
                    }
            );
            
            return returnActors;
        }
    }

    /**
     * Applys the given filters to all the actors that the filters accept.
     * Note that an Actor will be given to all Filters that accept it, even if
     * there are filters given which don't accept it.
     * @param filters ActionFilters to perform to all the actors.
     */
    public void applyToActors(final ActionFilter<A> ... filters)
    {
        final int filtersSize = filters.length;

        if (filters != null && filters.length > 0) {
            actActors.iterate(
                    new CallbackIterator<A>() {
                        public void iterate(final A actor)
                        {
                            for (int i = 0; i < filtersSize; ++i) {
                                final ActionFilter filter = filters[i];
                                if ( filter != null && filter.accept(actor) ) {
                                    filter.action( actor );
                                }
                            }
                        }
                    }
            );
        }
    }
    
    /**
     * Returns all the actors of the given class.
     * @param <B> The type of the class given.
     * @param klass The type of actor to get, cannot be null.
     * @return A list containing all the actors given. The list has the same generic type as the class given.
     */
    public <B> List<B> getActors(Class<B> klass)
    {
        return getActors(klass, EMPTY_CLASS_ARRAY);
    }

    /**
     * Gets all Actors which are an instance of the returnType and all of the
     * given klasses. The list returned uses the generic type of the given
     * returnType.
     *
     * Because the generic types used by this method are not derived from
     * Actors, you can get all Actors that adhere to an interface outside of
     * your Actor class hierarchy.
     * @param <B>
     * @param returnType
     * @param klasses
     * @return
     */
    public <B, C> List<B> getActors(final Class<B> returnType, final Class<C> ... klasses)
    {
        if (returnType == null) {
            throw new IllegalArgumentException("The returnType cannot be null.");
        }
        
        final int klassesSize = klasses.length;
        final List<B> returnActors = new LinkedList<B>();
        
        actActors.iterate(
                new CallbackIterator<A>() {
                    public void iterate(A actor)
                    {
                        if ( returnType.isInstance(actor) ) {
                            boolean match = true;
                            
                            for (int i = 0; i < klassesSize; ++i) {
                                final Class<C> klass = klasses[i];
                                if ( !klass.isInstance(actor) ) {
                                    match = false;
                                    break;
                                }
                            }
                            
                            if (match) {
                                returnActors.add( (B) actor );
                            }
                        }
                    }
                }
        );

        return returnActors;
    }

    /**
     * Retrieves the CollisionHandler being used by this World for handling it's
     * Actors collissions. Typically you don't need to retrieve this or mess
     * with it, unless your doing something special.
     * @return The CollisionHandler object that this World uses for finding collisions.
     */
    public CollisionHandler<A> getCollisions()
    {
        return collisions;
    }

    /**
     * Adds the actor and sets it's paint order. This is essentially a helper
     * function for doing this yourself.
     * @param actor The actor to add to this world, cannot be null.
     * @param paintOrder The paint order for the actor being painted.
     */
    public void addActor(A actor, int paintOrder)
    {
        addActor( actor );
        setPaintOrder( actor, paintOrder );
    }
    
    /**
     * This is a helper method that is the equivalent of calling addActor(actor)
     * and then setLocation(x, y) on that actor directly after.
     * It adds the given Actor and places it at the given location.
     * @param actor Adds the given actor.
     * @param x The x location for this actor.
     * @param y The y location for this actor.
     */
    public void addActor(A actor, float x, float y)
    {
        addActor( actor );
        actor.setLocation( x, y );
    }
    
    /**
     * Adds all of the given actors to this world. If any actor is null then an
     * exception will be thrown. This also occures if the actors array is null.
     * If actors is empty then nothing will happen.
     * @param actors The actors to add to this world.
     */
    public void addActor(A ... actors)
    {
        if (actors == null) {
            throw new IllegalArgumentException("The actors array cannot be null.");
        }

        for (int i = 0; i < actors.length; i++) {
            A actor = actors[i];
            if (actor == null) {
                throw new IllegalArgumentException("The actor cannot be null: " + i);
            }

            final World actorsWorld = actor.getWorld();
            if ( actorsWorld != null ) {
                actorsWorld.removeActorInner( actor );
            }
            
            addRemoveActors.add(actor);
            addRemoveActorsIsAdd.push(true);
        }
    }
    
    /**
     * This is the actual adding code that is performed when an
     * Actor really is added to the world.
     * @param actor The Actor to add to the World.
     */
    private void addActorInner(A actor)
    {
        final World actorsWorld = actor.getWorld();
        if (actorsWorld != this) {
            collisions.add(actor);
            actor.setWorld( this );

            Integer intObj = addingActorAct.remove(actor);
            if (intObj == null) {
                setActOrderInternal(actor, 0);
            } else {
                setActOrderInternal(actor, intObj);
            }

            intObj = addingActorPaint.remove(actor);
            if (intObj == null) {
                setPaintOrderInternal(actor, 0, true);
            } else {
                setPaintOrderInternal(actor, intObj, true);
            }
            
            actor.addedToWorld();
        }
    }

    /**
     * Removes all of the given actors from this World. If any of the Actors are
     * null then an exception is thrown.
     * @param actors The Actors to remove from this World.
     */
    public void removeActor(A ... actors)
    {
        if (actors == null) {
            throw new IllegalArgumentException("The actors array cannot be null.");
        }

        for (int i = 0; i < actors.length; i++) {
            A actor = actors[i];
            if (actor == null) {
                throw new IllegalArgumentException("None of the actors can be null: " + i);
            }
            
            if (!actor.isAwaitingRemoval()) {
                actor.setAwaitingRemoval(true);
                addRemoveActors.add(actor);
                addRemoveActorsIsAdd.push(false);
            }
        }
    }

    /**
     * Removes all the Actors that are currently in this world.
     */
    public void removeAllActors()
    {
        actActors.iterate( removeCallback );
    }

    /**
     * The actual code performed when an Actor is directly removed from this
     * World.
     * @param actor The Actor to remove from this World.
     */
    private void removeActorInner(A actor)
    {
        actActors.remove(actor, actor.getActOrder());
        paintActors.remove(actor, actor.getPaintOrder());
        collisions.remove(actor);
        actor.removedFromWorld();
        actor.setWorld(null);
        actor.setAwaitingRemoval(false);
    }

    /**
     * Updates the collisions information on the given actor within this World.
     * This should only be called by Actors that are already added to this World.
     * @param actor The Actor to update information about, cannot be null.
     */
    void updateActor(A actor)
    {
        collisions.update( actor );
    }
    
    /**
     * Sets the order that this Actor will act in. Actors with Low order values
     * Act first and then Actors with high values.
     * It's essentially the same as setPaintOrder but for act instead of paint.
     * @param actor The Actor to change the order for, cannot be null.
     * @param orderNumber The new order placement the Actor
     */
    public void setActOrder(A actor, int orderNumber)
    {
        if (actor == null) {
            throw new IllegalArgumentException("Actor cannot be null.");
        }
        
        if (orderNumber != actor.getActOrder()) {
            setActOrderInternal(actor, orderNumber);
        }
    }
    
    /**
     * Internal method that actually changes the Actors act order.
     * This presumes that the given parameters are valid.
     * @param actor
     * @param layerNumber
     */
    private final void setActOrderInternal(A actor, int layerNumber)
    {
        actActors.remove(actor, actor.getActOrder());
        actor.setActOrder(layerNumber);
        actActors.add(actor, layerNumber);
    }
    
    /**
     * Sets the order for the given Actor to be drawn at when painting.
     * Lower numbers are drawn first before high numbers. An Actor can only
     * have be in one paint layer.
     * @param actor The Actor to set to the paint layer. Cannot be null.
     * @param orderNumber The layer the Actor is set too.
     */
    public void setPaintOrder(A actor, int orderNumber)
    {
        if ( actor == null ) {
            throw new IllegalArgumentException("Actor cannot be null.");
        }
        
        if ( orderNumber != actor.getPaintOrder() ) {
            setPaintOrderInternal( actor, orderNumber );
        }
    }
    
    /**
     * Internall method to actually do the work of setting the Actor's paint
     * layer number.
     * @param actor The Actor to set to the paint layer. Cannot be null.
     * @param layerNumber The layer the Actor is set too.
     * @see World.setPaintLayer(Actor actor, int layerNumber)
     */
    private final void setPaintOrderInternal(A actor, int orderNumber)
    {
        setPaintOrderInternal(actor, orderNumber, false);
    }
    
    /**
     * The actual paint order setting code for the given Actor.
     * @param actor
     * @param orderNumber
     * @param force
     */
    private final void setPaintOrderInternal(A actor, int orderNumber, boolean force)
    {
        /* if it's not
         *   already in the world or
         *   waiting to be added to the world or
         *   forcefully being added */
        if (!paintActors.remove(actor, actor.getPaintOrder()) && !force) {
            if (addRemoveActors.contains(actor)) {
                addingActorPaint.put(actor, orderNumber);
            } else {
                throw new IllegalArgumentException("Actor is not in this world.");
            }
        } else {
            actor.setPaintOrder(orderNumber);
            paintActors.add(actor, orderNumber);
        }
    }
    
    /**
     * Scrolls the World's drawing view by the given amount.
     * @param moveScrollX The amount to scroll in the X direction.
     * @param moveScrollY The amount to scroll in the Y direction.
     */
    public void scroll(final float moveScrollX, final float moveScrollY)
    {
        setScroll(getScrollX() + moveScrollX, getScrollY() + moveScrollY);
    }
    
    /**
     * Sets the new scroll location for both X and Y axis. Equivalent to just
     * calling setScrollX and setScrollY sequentially.
     * @param scrollX The new amount that this is scrolled along the X axis.
     * @param scrollY The new amount that this is scrolled along the Y axis.
     */
    public void setScroll(final float scrollX, final float scrollY)
    {
        setScrollX( scrollX );
        setScrollY( scrollY );
    }
    
    /**
     * The amount that this World will be scrolled by when drawn.
     * @return How far this World has scrolled along the X axis.
     */
    public float getScrollX()
    {
        return scrollX;
    }
    
    /**
     * Sets the amount to scroll this World by, along the X-axis, when it is drawn.
     * @param scrollX The new amount that this is scrolled along the X axis.
     */
    public void setScrollX(final float scrollX)
    {
        this.scrollX = scrollX;
    }

    /**
     * Retrieves the amount to scroll this World by, along the Y axis, when
     * painting.
     * @return How far this World has scrolled along the Y axis.
     */
    public float getScrollY()
    {
        return scrollY;
    }
    
    /**
     * Sets how far to scroll this World by along the Y-axis, when painting.
     * @param scrollY The new amount that this is scrolled along the Y axis.
     */
    public void setScrollY(final float scrollY)
    {
        this.scrollY = scrollY;
    }
    
    /**
     * By default this is the equivalent of calling 'actActors()' method.
     * Override this to add user specific functionality.
     */
    @Override
    public void act()
    {
        actActors();
    }

    /**
     * Updates all of the Actors currently in this world by calling their
     * act method. This is performed in the order of their actLayer value.
     */
    public void actActors()
    {
        addRemoveActors();
        actActors.iterate( actActorsCallback );
        addRemoveActors();
    }

    /**
     * Paints all the actors contained within this world to the given graphics
     * object. The Actors are painted in the order of their layer.
     * @param g The Graphcis2D to paint this world's actors too.
     */
    public void paintActors(G g)
    {
        addRemoveActors();
        paintActorsCallback.g = g;
        paintActors.iterate( paintActorsCallback );
        addRemoveActors();
    }
    
    /**
     * This updates the internal list of actors, performing all the cached add
     * and removal of them. It's normally called automatically in the actActors
     * method, however it is exposed to allow sub-classes to call it if they
     * need to force the update of removal and adding of actors.
     */
    protected void addRemoveActors()
    {
        // swap the temp and current lists
        if (addRemoveActors.size() > 0) {
            final List<A> temp = tempAddRemoveActors;
            tempAddRemoveActors = addRemoveActors;
            addRemoveActors = temp;
            
            // swap the temp and current stack
            final BooleanStack tempStack = tempAddRemoveActorsIsAdd;
            tempAddRemoveActorsIsAdd = addRemoveActorsIsAdd;
            addRemoveActorsIsAdd = tempStack;
            
            for (int i = 0; i < tempAddRemoveActors.size(); i++) {
                final A actor = tempAddRemoveActors.get(i);
                if ( tempAddRemoveActorsIsAdd.peek(i) ) {
                    addActorInner( actor );
                } else {
                    removeActorInner(actor);
                }
            }
            
            // cleared for when we swap them on the next iteration
            // also to ensure we don't hang onto Actor references when we don't need to
            tempAddRemoveActors.clear();
            tempAddRemoveActorsIsAdd.clear();
        }
    }
    
    /**
     * If painting is enabled then it draws the contents of this world within
     * the set clip size.
     * @param g The GraphcsGL object to paint too.
     */
    @Override
    void realPaint(final G g)
    {
        if (isPaintEnabled()) {
            final float topLeftX = (getX() - getWidth()/2)  - getScrollX();
            final float topLeftY = (getY() - getHeight()/2) - getScrollY();

            g.translate( topLeftX ,  topLeftY );
            paint( g );
            g.translate( -topLeftX, -topLeftY );
        }
    }

    /**
     * By default this is the exact same as calling paintActors directly.
     * If overriden then the user should bear in mind that they will either have
     * to call super.paint or paintActors thenself in order to continue to paint
     * the Actors within this world.
     * @param g The Graphics2D to paint this World too.
     */
    @Override
    public void paint(final G g)
    {
        paintActors( g );
    }

    /**
     * Finds and returns all intersecting actors for the given actor, using the
     * CollisionsHandler inside of this World.
     * @param actor The actor to use to find intersecting actors.
     * @param collisionKlasses The types of actors to find that intersect the given actor.
     * @return A collection that contains all the actors, of the given type, that intersect the given actor.
     */
    public Collection<A> getIntersectingActors(A actor, Class<? extends A> ... collisionKlasses)
    {
        return collisions.getIntersectingActors(actor, collisionKlasses);
    }

    /**
     * Finds and returns the first actor that intersects the given actor, using
     * the CollissionsHandler of this World.
     * @param actor The actor to check intersections against.
     * @param collisionKlasses The types of actors to search for.
     * @return Null if no intersecting actor was found, otherwise the first actor that does.
     */
    public A getIntersectingActor(A actor, Class<? extends A> ... collisionKlasses)
    {
        return collisions.getIntersectingActor(actor, collisionKlasses);
    }

    /**
     * Using the CollissionsHandler in this world, this will apply the given
     * action too all actors that intersect the given actor. It will use the
     * collisionKlasses given to find the actors check for intersections.
     * @param actor The actor to search for intersections against.
     * @param action The action to apply to all intersecting actors.
     * @param collisionKlasses The types of classes to check for intersections against.
     * @return True if one or more actors were run in the given action.
     */
    public boolean applyToIntersectingActors(A actor, Action<A> action, Class<? extends A> ... collisionKlasses)
    {
        return collisions.applyToIntersectingActors( actor, action, collisionKlasses );
    }

    /**
     * The same as applyToIntersectingActors, only this applies the action to
     * the first actor found and no more.
     * @param actor The actor to check intersections against.
     * @param action The action to apply to the intersecting actor.
     * @param collisionKlasses The types of actors for check for.
     * @return True if the given action was applied to an intersecting actor, otherwise false.
     */
    public boolean applyToIntersectingActor(A actor, Action<A> action, Class<? extends A> ... collisionKlasses)
    {
        return collisions.applyToIntersectingActor( actor, action, collisionKlasses );
    }

    /**
     * The PaintCallback class is the callback iterator for when painting all
     * of the actors in the paintActors class.
     */
    private class PaintCallback implements CallbackIterator<A> {
        private G g;

        public void iterate(final A actor)
        {
            if (!actor.isAwaitingRemoval()) {
                actor.realPaint( g );
            }
        }
    };

    /**
     * Handles a click event, by default this will simply pass the event on to
     * it's children.
     *
     * @param event The event to handle.
     */
    @Override
    protected void handleClickEvent(ControlClickEvent event)
    {
        handleClickEventIterator.event = event;
        paintActors.iterateControlCallback( handleClickEventIterator );
    }

    /**
     * Handles a key event, by default this will simply pass the event on to
     * it's children.
     *
     * @param event The key event to handle.
     */
    @Override
    protected void handleKeyEvent(ControlKeyEvent event)
    {
        handleKeyEventIterator.event = event;
        paintActors.iterateControlCallback( handleKeyEventIterator );
    }

    /**
     * Handles a key event, by default this will simply pass the event on to
     * it's children.
     *
     * @param event The key event to handle.
     */
    @Override
    protected void handleKeyCharEvent(ControlKeyCharEvent event)
    {
        handleKeyCharEventIterator.event = event;
        paintActors.iterateControlCallback( handleKeyCharEventIterator );
    }

    /**
     * Handled a mouse motion event, by default this will simply pass the event
     * on to it's children.
     *
     * @param event The motion event to handle.
     */
    @Override
    protected void handleMotionEvent(ControlMotionEvent event)
    {
        handleMotionEventIterator.event = event;
        paintActors.iterateControlCallback( handleMotionEventIterator );
    }
}
